Add "gpgkeypath" option to remotes
authorColin Walters <walters@verbum.org>
Wed, 16 Nov 2016 14:13:54 +0000 (09:13 -0500)
committerAtomic Bot <atomic-devel@projectatomic.io>
Thu, 17 Nov 2016 09:44:07 +0000 (09:44 +0000)
For Project Atomic, we already have RPM signatures which use files in
`/etc/pki/rpm-gpg`.  It's convenient to simply bind the OSTree remote
configuration to those file paths, rather than having duplicate key
data.

This does mean that we need to parse the files for verification, so we
end up importing them into the verifier's temporary keyring, which is
a bit ugly, but it's what other projects do.

Closes: https://github.com/ostreedev/ostree/issues/573
Closes: #575
Approved by: giuseppe

man/ostree.repo-config.xml
man/ostree.xml
src/libostree/ostree-gpg-verifier.c
src/libostree/ostree-gpg-verifier.h
src/libostree/ostree-repo.c
tests/test-remote-gpg-import.sh

index 1182aa93e9e3a61196a1125af6183a1a61cfb498..876c9dfc4559bddf0ebb8fa1f57e58c47ee65eab 100644 (file)
@@ -199,7 +199,8 @@ Boston, MA 02111-1307, USA.
   <refsect1>
     <title>Per-remote GPG keyrings and verification</title>
     <para>
-      OSTree supports a per-remote GPG keyring.  For more information see
+      OSTree supports a per-remote GPG keyring, as well as a
+      <literal>gpgkeypath</literal> option.  For more information see
       <citerefentry><refentrytitle>ostree</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
       in the section <literal>GPG verification</literal>.
     </para>
index 80b0b0c109450c4b8d5474ae5020cfbbb7ec1365..e31d58b296fb4d1c442cb5b124914d85059a83f9 100644 (file)
@@ -433,8 +433,12 @@ Boston, MA 02111-1307, USA.
           in this directory.
         </para>
         <para>
-           In addition to the system repository, OSTree supports a
-           per-remote
+         In addition to the system repository, OSTree supports two
+         other paths.  First, there is a
+         <literal>gpgkeypath</literal> option for remotes, which must
+         point to the filename of an ASCII-armored key.
+       </para>
+       <para>Second, there is support for a per-remote
            <filename><replaceable>remotename</replaceable>.trustedkeys.gpg</filename>
            file stored in the toplevel of the repository (alongside
            <filename>objects/</filename> and such). This is
index 9eae7ceb01ee0d4ea15823ddffbc57e69d2e270f..eda05e9a1a8efe31b8cfd436a51ef3521dd9d59b 100644 (file)
@@ -40,6 +40,7 @@ struct OstreeGpgVerifier {
   GObject parent;
 
   GList *keyrings;
+  GPtrArray *key_ascii_files;
 };
 
 G_DEFINE_TYPE (OstreeGpgVerifier, _ostree_gpg_verifier, G_TYPE_OBJECT)
@@ -50,6 +51,8 @@ ostree_gpg_verifier_finalize (GObject *object)
   OstreeGpgVerifier *self = OSTREE_GPG_VERIFIER (object);
 
   g_list_free_full (self->keyrings, g_object_unref);
+  if (self->key_ascii_files)
+    g_ptr_array_unref (self->key_ascii_files);
 
   G_OBJECT_CLASS (_ostree_gpg_verifier_parent_class)->finalize (object);
 }
@@ -98,6 +101,7 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier  *self,
   OstreeGpgVerifyResult *result = NULL;
   gboolean success = FALSE;
   GList *link;
+  int armor;
 
   /* GPGME has no API for using multiple keyrings (aka, gpg --keyring),
    * so we concatenate all the keyring files into one pubring.gpg in a
@@ -149,6 +153,44 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier  *self,
   if (!g_output_stream_close (target_stream, cancellable, error))
     goto out;
 
+  /* Save the previous armor value - we need it on for importing ASCII keys */
+  armor = gpgme_get_armor (result->context);
+  gpgme_set_armor (result->context, 1);
+
+  /* Now, use the API to import ASCII-armored keys */
+  if (self->key_ascii_files)
+    {
+      for (guint i = 0; i < self->key_ascii_files->len; i++)
+        {
+          const char *path = self->key_ascii_files->pdata[i];
+          glnx_fd_close int fd = -1;
+          ot_auto_gpgme_data gpgme_data_t kdata = NULL;
+
+          fd = openat (AT_FDCWD, path, O_RDONLY | O_CLOEXEC) ;
+          if (fd < 0)
+            {
+              glnx_set_prefix_error_from_errno (error, "Opening %s", path);
+              goto out;
+            }
+
+          gpg_error = gpgme_data_new_from_fd (&kdata, fd);
+          if (gpg_error != GPG_ERR_NO_ERROR)
+            {
+              ot_gpgme_error_to_gio_error (gpg_error, error);
+              goto out;
+            }
+
+          gpg_error = gpgme_op_import (result->context, kdata);
+          if (gpg_error != GPG_ERR_NO_ERROR)
+            {
+              ot_gpgme_error_to_gio_error (gpg_error, error);
+              goto out;
+            }
+        }
+    }
+
+  gpgme_set_armor (result->context, armor);
+
   /* Both the signed data and signature GBytes instances will outlive the
    * gpgme_data_t structs, so we can safely reuse the GBytes memory buffer
    * directly and avoid a copy. */
@@ -225,6 +267,15 @@ _ostree_gpg_verifier_add_keyring (OstreeGpgVerifier  *self,
   self->keyrings = g_list_append (self->keyrings, g_object_ref (path));
 }
 
+void
+_ostree_gpg_verifier_add_key_ascii_file (OstreeGpgVerifier *self,
+                                         const char        *path)
+{
+  if (!self->key_ascii_files)
+    self->key_ascii_files = g_ptr_array_new_with_free_func (g_free);
+  g_ptr_array_add (self->key_ascii_files, g_strdup (path));
+}
+
 gboolean
 _ostree_gpg_verifier_add_keyring_dir (OstreeGpgVerifier   *self,
                                       GFile               *path,
index 2db39f3b5e588b488480f121302fffecf2d3b0b3..4156d1bd43c4efc3f83067241390b90da8a9d50b 100644 (file)
@@ -62,4 +62,7 @@ gboolean      _ostree_gpg_verifier_add_global_keyring_dir (OstreeGpgVerifier  *s
 void _ostree_gpg_verifier_add_keyring (OstreeGpgVerifier *self,
                                        GFile             *path);
 
+void _ostree_gpg_verifier_add_key_ascii_file (OstreeGpgVerifier *self,
+                                              const char        *path);
+
 G_END_DECLS
index 584e570ce90f1cd0fb0b1ff6600f785c1f60ee40..c0cbede6a6465e044e4cc267331e43c7078ed148 100644 (file)
@@ -4282,6 +4282,7 @@ _ostree_repo_gpg_verify_data_internal (OstreeRepo    *self,
     }
   else if (remote_name != NULL)
     {
+      g_autofree char *gpgkeypath = NULL;
       /* Add the remote's keyring file if it exists. */
 
       OstreeRemote *remote;
@@ -4299,6 +4300,13 @@ _ostree_repo_gpg_verify_data_internal (OstreeRepo    *self,
           add_global_keyring_dir = FALSE;
         }
 
+      if (!ot_keyfile_get_value_with_default (remote->options, remote->group, "gpgkeypath", NULL,
+                                              &gpgkeypath, error))
+        return NULL;
+
+      if (gpgkeypath)
+        _ostree_gpg_verifier_add_key_ascii_file (verifier, gpgkeypath);
+
       ost_remote_unref (remote);
     }
 
index 8d155f7972040b7b0f08bdda7a5acebb97396b5c..4dbe7fd9d06a103868f2ebe2c0744415a4dfe387 100755 (executable)
@@ -26,7 +26,7 @@ unset OSTREE_GPG_HOME
 
 setup_fake_remote_repo1 "archive-z2"
 
-echo "1..1"
+echo "1..2"
 
 cd ${test_tmpdir}
 mkdir repo
@@ -143,5 +143,32 @@ if ${OSTREE} pull R2:main >/dev/null 2>&1; then
 fi
 ${OSTREE} pull R3:main >/dev/null
 
-libtest_cleanup_gpg
 echo "ok"
+
+rm repo/refs/remotes/* -rf
+${OSTREE} prune --refs-only
+
+# Test the successful gpgkeypath option
+${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/key3.asc R4 $(cat httpd-address)/ostree/gnomerepo
+${OSTREE} pull R4:main >/dev/null
+
+rm repo/refs/remotes/* -rf
+${OSTREE} prune --refs-only
+
+${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/INVALIDKEYPATH.asc R5 $(cat httpd-address)/ostree/gnomerepo
+if ${OSTREE} pull R5:main 2>err.txt; then
+    assert_not_reached "Unexpectedly succeeded at pulling with nonexistent key"
+fi
+assert_file_has_content err.txt "INVALIDKEYPATH.*No such file or directory"
+
+rm repo/refs/remotes/* -rf
+${OSTREE} prune --refs-only
+
+${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/key2.asc R6 $(cat httpd-address)/ostree/gnomerepo
+if ${OSTREE} pull R6:main 2>err.txt; then
+    assert_not_reached "Unexpectedly succeeded at pulling with different key"
+fi
+assert_file_has_content err.txt "GPG signatures found, but none are in trusted keyring"
+
+echo "ok"
+libtest_cleanup_gpg